// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CodeStyleSettingsManager.cs" company="Roman '4ux-nbIx' Novitsky">
//   Copyright (c) Roman '4ux-nbIx' Novitsky 2009
// </copyright>
// <summary>
//   Defines the CodeStyleSettingsManager type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace SettingsManager.ComponentSettingsManagers
{
  #region Namespace Imports

  using System.Collections.Generic;
  using System.Linq;
  using System.Xml;

  using JetBrains.Annotations;
  using JetBrains.ComponentModel;
  using JetBrains.ReSharper.Psi.CodeStyle;
  using JetBrains.ReSharper.Psi.Impl.CodeStyle;

  #endregion


  /// <summary>
  /// Describes and manipulates the code style settings manager.
  /// </summary>
  internal sealed class CodeStyleSettingsManager : ResharperComponentSettingsManager
  {
    #region Constructors and Destructors

    /// <summary>
    /// Initializes a new instance of the <see cref="CodeStyleSettingsManager"/> class.
    /// </summary>
    public CodeStyleSettingsManager()
      : base("CodeStyleSettings", "Code Style Settings")
    {
    }

    #endregion


    #region Public Methods

    /// <summary>
    /// Loads the settings.
    /// </summary>
    /// <param name="component">
    ///   The component.
    /// </param>
    /// <param name="element">
    ///   The element.
    /// </param>
    /// <param name="options"></param>
    public override void LoadSettings(IXmlExternalizableComponent component, XmlElement element, object options)
    {
      var codeStyleSettingsManager = component as JetBrains.ReSharper.Psi.CodeStyle.CodeStyleSettingsManager;

      if (codeStyleSettingsManager != null)
      {
        component.ReadFromXml(element);
        codeStyleSettingsManager.CodeStyleSettings = null;

        return;
      }

      var solutionCodeStyleSettings = component as SolutionCodeStyleSettingsImpl;

      if (solutionCodeStyleSettings != null && solutionCodeStyleSettings.Sharing.Value != CodeStyleSettingsSharing.USER)
      {
        solutionCodeStyleSettings.ReadFromXml(element);
      }
    }


    /// <summary>
    /// Saves the settings.
    /// </summary>
    /// <param name="component">The component.</param>
    /// <param name="element">The element.</param>
    /// <param name="options">The options.</param>
    public override void SaveSettings(IXmlExternalizableComponent component, XmlElement element, object options)
    {
      var solutionCodeStyleSettings = component as SolutionCodeStyleSettingsImpl;

      if (solutionCodeStyleSettings != null && solutionCodeStyleSettings.Sharing.Value != CodeStyleSettingsSharing.USER)
      {
        solutionCodeStyleSettings.WriteToXml(element);

        return;
      }

      var codeStyleSettingsManager =
        JetBrains.ReSharper.Psi.CodeStyle.CodeStyleSettingsManager.Instance as CodeStyleSettingsManagerImpl;

      if (codeStyleSettingsManager != null)
      {
        codeStyleSettingsManager.WriteToXml(element);
      }
    }


    /// <summary>
    /// Updates the settings.
    /// </summary>
    /// <param name="component">The component.</param>
    /// <param name="componentSettingsNode">The component settings node.</param>
    /// <param name="options">The options.</param>
    /// <returns>
    /// Returns <c>true</c>, if there were any changes.
    /// </returns>
    public override bool UpdateSettings(IXmlExternalizableComponent component, XmlElement componentSettingsNode, [CanBeNull] object options)
    {
      if (componentSettingsNode.OwnerDocument == null)
      {
        return false;
      }

      XmlElement currentSettings = componentSettingsNode.OwnerDocument.CreateElement("temp");

      SaveSettings(component, currentSettings, options);

      bool customDictionariesChanged = UpdateCustomDictionaries(currentSettings, componentSettingsNode);
      bool abbreviationSettingsChanged = UpdateAbbreviationSettings(currentSettings, componentSettingsNode);

      return customDictionariesChanged || abbreviationSettingsChanged;
    }

    #endregion


    #region Methods

    [NotNull]
    private static IEnumerable<XmlNode> GetAbbreviationNodes([NotNull] XmlNode xmlElement)
    {
      return xmlElement.ChildNodes.Cast<XmlNode>().Where(childNode => childNode.Name == "Abbreviation").ToList();
    }


    [CanBeNull]
    private static XmlElement GetCustomDictionariesElement([NotNull] XmlNode element)
    {
      XmlElement agentSmithCSharpStyleSettings = element["AgentSmithCSharpStyleSettings"];

      if (agentSmithCSharpStyleSettings == null)
      {
        return null;
      }

      XmlElement codeStyleSettings = agentSmithCSharpStyleSettings["CodeStyleSettings"];

      return codeStyleSettings != null ? codeStyleSettings["CustomDictionaries"] : null;
    }


    [CanBeNull]
    private static XmlElement GetNamingElement([NotNull] XmlNode xmlElement)
    {
      return xmlElement["Naming2"];
    }


    private static bool UpdateAbbreviationSettings(
      [NotNull] XmlNode currentSettings, [NotNull] XmlNode componentSettingsNode)
    {
      bool changed = false;

      changed |= UpdateAbbreviations(currentSettings, componentSettingsNode);

      changed |= UpdateAbbreviations("CSharp", currentSettings, componentSettingsNode);
      changed |= UpdateAbbreviations("VB", currentSettings, componentSettingsNode);
      changed |= UpdateAbbreviations("Web", currentSettings, componentSettingsNode);
      changed |= UpdateAbbreviations("Xaml", currentSettings, componentSettingsNode);

      return changed;
    }


    private static bool UpdateAbbreviations(
      [CanBeNull] XmlNode currentSettings, [CanBeNull] XmlNode componentSettingsNode)
    {
      if (currentSettings == null || componentSettingsNode == null)
      {
        return false;
      }

      XmlElement currentNamingSettings = GetNamingElement(currentSettings);
      XmlElement namingSettings = GetNamingElement(componentSettingsNode);

      if (currentNamingSettings == null || namingSettings == null)
      {
        return false;
      }

      var existingNodes = GetAbbreviationNodes(namingSettings);
      var newAbbreviations = GetAbbreviationNodes(currentNamingSettings);

      if (!AbbreviationsChanged(existingNodes, newAbbreviations))
      {
        return false;
      }

      foreach (XmlNode existingNode in existingNodes)
      {
        namingSettings.RemoveChild(existingNode);
      }

      foreach (XmlNode abbreviation in newAbbreviations)
      {
        XmlNode node = abbreviation.CloneNode(true);
        namingSettings.AppendChild(node);
      }

      return true;
    }


    private static bool AbbreviationsChanged(IEnumerable<XmlNode> existingNodes, IEnumerable<XmlNode> newAbbreviations)
    {
      if (existingNodes.Count() != newAbbreviations.Count())
      {
        return true;
      }

      return newAbbreviations.Any(newAbbreviation => !existingNodes.Where(n => n.InnerXml == newAbbreviation.InnerXml).Any());
    }


    private static bool UpdateAbbreviations(
      [NotNull] string element, [NotNull] XmlNode currentSettings, [NotNull] XmlNode componentSettingsNode)
    {
      XmlElement currentSharpSettingsNode = currentSettings[element];
      XmlElement sharpSettingsNode = componentSettingsNode[element];

      return UpdateAbbreviations(currentSharpSettingsNode, sharpSettingsNode);
    }


    private static bool UpdateCustomDictionaries(
      [NotNull] XmlNode currentSettings, [NotNull] XmlNode componentSettingsNode)
    {
      XmlElement currentCustomDictionariesSettings = GetCustomDictionariesElement(currentSettings);
      XmlElement customDictionariesSettings = GetCustomDictionariesElement(componentSettingsNode);

      if (currentCustomDictionariesSettings != null && customDictionariesSettings != null)
      {
        if (customDictionariesSettings.InnerXml != currentCustomDictionariesSettings.InnerXml)
        {
          customDictionariesSettings.InnerXml = currentCustomDictionariesSettings.InnerXml;
          return true;
        }
      }

      return false;
    }

    #endregion
  }
}